/*
 * Copyright (c) 2020 Fraunhofer FOKUS and others. All rights reserved.
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contact: mosaic@fokus.fraunhofer.de
 */

package org.eclipse.mosaic.fed.mapping.ambassador;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.eclipse.mosaic.fed.mapping.ambassador.spawning.VehicleTypeSpawner;
import org.eclipse.mosaic.fed.mapping.config.CPrototype;
import org.eclipse.mosaic.fed.mapping.config.units.CSumoVehicle;
import org.eclipse.mosaic.interactions.mapping.SumoVehicleRegistration;
import org.eclipse.mosaic.lib.objects.vehicle.VehicleType;
import org.eclipse.mosaic.lib.util.NameGenerator;
import org.eclipse.mosaic.rti.api.IllegalValueException;
import org.eclipse.mosaic.rti.api.Interaction;
import org.eclipse.mosaic.rti.api.InternalFederateException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;

/**
 * Class for creating vehicleRegistrations of cars in sumo
 */
public class SumoVehicleGenerator {
    private static final Logger LOG = LoggerFactory.getLogger(SumoVehicleGenerator.class);

    /**
     * List of lanes to be used. The vehicles will be evenly distributed among
     * the given lanes. When no value is given lane zero will be used for all
     * vehicles.
     */
    private final String name;
    private final VehicleTypeSpawner type;
    private final String group;

    public SumoVehicleGenerator(CSumoVehicle vehicleConfiguration) {
        this.name = vehicleConfiguration.name;
        this.type = createPrototype(vehicleConfiguration);
        this.group = vehicleConfiguration.group;
    }

    private VehicleTypeSpawner createPrototype(CSumoVehicle vehicleConfiguration) {
        CPrototype prototypeConfiguration = vehicleConfiguration.type;
        if (prototypeConfiguration != null) {
            // Generate internal type
            return new VehicleTypeSpawner(prototypeConfiguration);
        }
        return null;
    }

    void fillInPrototype(SpawningFramework framework) {
        if (type != null) {
            CPrototype prototypeConfiguration = framework.getPrototypeByName(type.getPrototype());
            if (prototypeConfiguration == null) {
                return;
            }
            type.fillInPrototype(prototypeConfiguration);
        }
    }

    void collectVehicleTypes(HashMap<String, VehicleType> types) {
        if (type != null) {
            String key = type.getPrototype();
            if (types.containsKey(key) && (type.convertType().equals(types.get(key)))) {
                return;
            }
            if ((key == null) || types.containsKey(key)) {
                key = NameGenerator.getPrototypeName(type.getPrototype());
                type.setPrototype(key);
            }
            types.put(key, type.convertType());
            LOG.info("Registering Vehicle Type: {}", types.get(key));
        }
    }


    /**
     * This contains the main logic of vehicle spawning.
     *
     * @param framework the {@link SpawningFramework} handling the time advance
     * @return true if there is no more vehicles to spawn or max time reached, thus the vehicle spawner can be removed
     * @throws InternalFederateException thrown if time advance couldn't be completed successfully
     */
    boolean timeAdvance(SpawningFramework framework) throws InternalFederateException {
        // because here wo are only create registration
        // wo should create all at the beginning time, no need waiting for starting time like wo setting in vehicleFlow
        // so wo should not add 'rti.timeAdvance' here


        createVehicle(framework, name, group, type);

        return true;
    }


    private void createVehicle(SpawningFramework framework, String name, String group, VehicleTypeSpawner type)
            throws InternalFederateException {
        // if no group is defined in vehicle definition take group declared in prototype
        String spawningGroup = ObjectUtils.defaultIfNull(group, type.getGroup());


        Interaction interaction = new SumoVehicleRegistration(framework.getTime(), name, spawningGroup,
                type.getAppList(), type.convertType()
            );

        try {
            LOG.info("Creating SUMO Vehicle: time={},name={},applications={}", framework.getTime(), name,
                    type.getAppList());
            framework.getRti().triggerInteraction(interaction);
        } catch (IllegalValueException e) {
            LOG.error("Couldn't send an {} interaction in VehicleStreamGenerator.timeAdvance()", interaction.getTypeId(), e);
            throw new InternalFederateException("Exception in VehicleStreamGenerator.timeAdvance()", e);
        }
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
                .append("name", name)
                .append("type", type)
                .append("group", group)
                .toString();
    }
}
